package net.lastcoder.ifserver.game;

import net.lastcoder.ifserver.script.ScriptManage;
import net.lastcoder.ifserver.util.TaskEngine;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

/**
 * @author WenYF
 * @email wenyongfu@huohaoyun.cn
 * @since 2021/9/30
 */
public class GameManager {
    private static final Logger Log = LoggerFactory.getLogger(GameManager.class);

    private static class SingletonHolder {
        private static final GameManager instance = new GameManager();
    }

    public static GameManager getInstance() {
        return SingletonHolder.instance;
    }
    
    private Map<String, Client> clients;
    private Map<String, Role> roles;

    private ExecutorService actionsService;
    
    private int fps = 30;

    /**
     * 处理器
     */
    private Map<String, BiConsumer<Client, String>> actionHandlerMap;

    /**
     * 处理器
     */
    private Map<String, Runnable> frameHandlerMap;
    
    public GameManager() {
        clients = new HashMap<>();
        roles = new HashMap<>();
        actionHandlerMap = new ConcurrentHashMap<>();
        frameHandlerMap = new ConcurrentHashMap<>();
        actionsService = Executors.newSingleThreadExecutor();
    }
    
    public void start() {
        TaskEngine.getInstance().scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                try {
                    // 脚本
                    ScriptManage.getInstance().processFrame();

                    // 插件
                    for (Runnable handler : frameHandlerMap.values()) {
                        handler.run();
                    }
                } catch (Exception e) {
                    Log.error(e.getLocalizedMessage(), e);
                }
            }
        }, 0, 1000 / fps);
    }
    
    public void processClientMessage(Client client, String msg) {
        actionsService.execute(() -> {
            // 脚本
            ScriptManage.getInstance().processMessage(client, msg);

            // 插件
            for (BiConsumer<Client, String> handler : actionHandlerMap.values()) {
                handler.accept(client, msg);
            }
        });
    }
    
    public void recvMessage(String userid, String msg) {
        Client client = clients.get(userid);
        processClientMessage(client, msg);
    }
    
    public void addClient(Client client) {
        clients.put(client.getUserid(), client);
        roles.put(client.getUserid(), client.getRole());
    }
    
    public void removeClientAndRole(String userid) {
        Client client = clients.remove(userid);
        roles.remove(userid);
        
        if (client != null) {
            JSONObject json = new JSONObject();
            json.put("action", "roleleave");
            JSONObject params = new JSONObject();
            params.put("userid", client.getUserid());
            json.put("params", params);
            processClientMessage(client, json.toString());
        }
    }
    
    public Role getRole(String userid) {
        return roles.get(userid);
    }

    public Collection<Role> getRoles() {
        Collection<Role> values = roles.values();
        Iterator<Role> iterator = values.iterator();
        while(iterator.hasNext()) {
            iterator.next();
        }

        return values;
    }

    public void addRole(String userid, String color, int x, int y) {
        Role role = new Role();
        role.pos = new Vector2(x, y);
        role.userid = userid;
        role.color = color;
        
        roles.put(userid, role);
        getClient(userid).setRole(role);
    }
    
    public Client getClient(String userid) {
        return clients.get(userid);
    }
    
    public Collection<Client> getClients() {
        return clients.values();
    }
    
    public void putActionHandler(String name, BiConsumer<Client, String> handler) {
        actionHandlerMap.put(name, handler);
    }
    
    public void removeActionHandler(String name) {
        actionHandlerMap.remove(name);
    }
    
    public void putFrameHandler(String name, Runnable handler) {
        frameHandlerMap.put(name, handler);
    }
    
    public void removeFrameHandler(String name) {
        frameHandlerMap.remove(name);
    }
    
    public void broadcast(String msg) {
        try {
            for (Client client : clients.values()) {
                client.send(msg);
            }
        } catch (IOException e) {
            Log.error(e.getLocalizedMessage(), e);
        }
    }
    
    public void send(String userid, String msg) {
        Client client = getClient(userid);
        if (client == null) {
            return;
        }
        try {
            client.send(msg);
        } catch (IOException e) {
            Log.error(e.getLocalizedMessage(), e);
        }
    }
}
